package tw;

import java.applet.Applet;
import java.awt.*;

public class PendulumSystem extends Applet implements Runnable {
	private double gravityCst;
	private double massCst;
	Thread animThread;
	Dimension area;
	Button b;
	Color bgColor;
	Image bgImage;
	boolean changeGravity;
	boolean changeLength;
	boolean changeMass;
	int count;
	Checkbox Cshow;
	double cst;
	long delay;
	long delta;
	boolean dragging;
	Image fgImage;
	boolean forceFlag;
	Graphics gb;
	Graphics gf;
	double g;
	int itmp;
	long lastTime;
	double mass;
	TextField mouseP;
	Dimension offDimension;
	int offset;
	Pendulum P;
	double period;
	int prevx;
	int prevy;
	boolean rightButton;
	String rts;
	boolean running;
	int size;
	int size2;
	int size3;
	long startTime;
	String STR[] = { "Reset", "Pause", "Show", "Resume" };
	int x0;
	int xmax;
	double xs;
	int y0;
	double ys;

	public PendulumSystem() {
		offset = 50;
		g = 980D;
		mass = 1.0D;
		forceFlag = false;
		bgColor = Color.lightGray;
		size = 3;
		size2 = 2 * size;
		size3 = size2;
		gravityCst = 0.10000000000000001D;
		massCst = 50D;
		period = 0.0D;
		count = 0;
		startTime = 0L;
		delay = 50L;
		running = true;
		changeLength = false;
		changeGravity = false;
		changeMass = false;
		dragging = false;
		rightButton = false;
		cst = 57.295779513082323D;
		this.setSize(300, 200);
	}

	public boolean action(Event event, Object obj) {
		if (event.target instanceof Button) {
			String s = (String) obj;
			if (s.equals(STR[0])) {
				running = true;
				b.setLabel(STR[1]);
				reset(true);
			} else if (s.equals(STR[1])) {
				running = false;
				b.setLabel(STR[3]);
			} else if (s.equals(STR[3])) {
				running = true;
				b.setLabel(STR[2]);
			}
		} else if (event.target == Cshow)
			forceFlag = Cshow.getState();
		return true;
	}

	public void init() {
		for (int i = 0; i < STR.length; i++)
			if ((rts = getParameter(STR[i])) != null)
				STR[i] = new String(rts);

		setBackground(bgColor);
		Panel panel = new Panel();
		panel.add(mouseP = new TextField("0 , 0", 24));
		panel.add(new Button(STR[0]));
		panel.add(b = new Button(STR[1]));
		panel.add(Cshow = new Checkbox(STR[2]));
		add("North", panel);
		P = new Pendulum(g);
		delay = 30L;
		reset(true);
	}

	public boolean mouseDown(Event event, int i, int j) {
		j -= offset;
		if (!running)
			return running = true;
		if (j < -size)
			return true;
		if (event.modifiers == 4) {
			rightButton = true;
			writeText(i, j);
			return true;
		}
		rightButton = false;
		running = false;
		if (Math.sqrt((i - x0) * (i - x0) + (j - y0) * (j - y0)) < (double) size2)
			changeLength = true;
		else if (Math.abs(i - xmax) < 5)
			if (Math.abs((double) j - gravityCst * g) < 5D)
				changeGravity = true;
			else if ((double) (j - area.height / 2) - massCst * mass < 5D)
				changeMass = true;
		drawPendulum(xs, ys, true);
		dragging = true;
		if (!changeGravity && !changeMass) {
			xs = i;
			ys = j;
		}
		drawPendulum(xs, ys, false);
		mouseDrag(event, i, j);
		return true;
	}

	public boolean mouseDrag(Event event, int i, int j) {
		j -= offset;
		if (running)
			return running;
		if (rightButton)
			return true;
		drawPendulum(xs, ys, true);
		if (changeMass && j > area.height / 2) {
			drawMass(bgColor);
			mass = (double) (j - area.height / 2) / massCst;
			size3 = (int) Math.max(Math.sqrt(mass) * (double) size2, 2D);
			drawMass(Color.black);
		} else if (changeGravity) {
			drawGravity(bgColor);
			g = (double) j / gravityCst;
			drawGravity(Color.red);
		} else {
			if (!validXY(i, j))
				return true;
			xs = i;
			if (changeLength) {
				ys = j;
				setpendulum(xs, ys, true);
			} else {
				ys = (double) y0 + Math.sqrt(P.length * P.length - (xs - (double) x0) * (xs - (double) x0));
				setpendulum(xs, ys, false);
			}
			writeText(xs, ys);
		}
		drawPendulum(xs, ys, false);
		repaint();
		return true;
	}

	public boolean mouseMove(Event event, int i, int j) {
		writeText(i, j - offset);
		return true;
	}

	public boolean mouseUp(Event event, int i, int j) {
		j -= offset;
		if (rightButton)
			return true;
		clearPlot(j);
		drawGravity(Color.red);
		drawMass(Color.black);
		dragging = false;
		if (changeLength) {
			gb.setColor(Color.red);
			gb.fillOval(x0 - size, 0, size2, size2);
			setpendulum(xs, ys, true);
		} else if (changeGravity) {
			P.setG(g);
			changeGravity = false;
		} else if (changeMass)
			changeMass = false;
		changeLength = false;
		count = 0;
		period = 0.0D;
		running = true;
		b.setLabel("pause");
		return true;
	}

	public void paint(Graphics g) {
		update(g);
	}

	public void reset(boolean flag) {
		area = this.getSize();
		area.height -= offset;
		if (gf == null) {
			offDimension = area;
			bgImage = createImage(area.width, area.height);
			gb = bgImage.getGraphics();
			fgImage = createImage(area.width, area.height);
			gf = fgImage.getGraphics();
			x0 = area.width / 2;
			y0 = 0;
		}
		xs = (double) x0 + (double) x0 / 3D;
		ys = (double) area.height * 0.65000000000000002D;
		xmax = 2 * x0 - 21;
		setpendulum(xs, ys, true);
		g = 980D;
		mass = 1.0D;
		size3 = size2;
		init_plot();
		start();
	}

	public void run() {
		Thread.currentThread().setPriority(1);
		for (; Thread.currentThread() == animThread;) {
			delta = System.currentTimeMillis() - lastTime;
			if (running)
				advanced((double) delta / 1000D);
			lastTime += delta;
			startTime += delay;
			try {
				Thread.sleep(Math.max(0L, startTime - System.currentTimeMillis()));
				continue;
			} catch (InterruptedException interruptedexception) {
			}
			break;
		}

	}

	public void start() {
		P.setG(g);
		if (running)
			drawPeroid(true, true, 0, 0);
		count = 0;
		period = 0.0D;
		if (running && animThread == null) {
			animThread = new Thread(this);
			animThread.start();
			lastTime = startTime = System.currentTimeMillis();
		}
	}

	public void stop() {
		animThread = null;
	}

	public void update(Graphics g) {
		g.drawImage(fgImage, 0, offset, this);
	}

	void advanced(double d) {
		drawPendulum(xs, ys, true);
		if ((period = P.move(d)) > 0.0D) {
			int i = area.width / 2 + (int) (P.length * Math.sin(P.theta0));
			int j = (int) (period * 100D);
			drawPeroid(true, false, i, j);
			count = count + 1;
			if (count > 1)
				drawPeroid(false, false, i, j);
		}
		xs = (double) x0 + P.xc;
		ys = (double) y0 + P.yc;
		drawPendulum(xs, ys, false);
	}

	void clear() {
		gb.setColor(bgColor);
		gb.fillRect(0, 0, area.width, area.height);
		gb.setColor(Color.black);
		gb.drawRect(0, 0, area.width - 1, area.height - 1);
		byte byte0 = 5;
		gb.drawLine(byte0, 0, byte0, area.height);
		for (int i = area.height; i > 0; i -= 10)
			gb.drawLine(byte0, i, byte0 + 5, i);

		for (int j = area.height; j > 0; j -= 50) {
			gb.drawLine(byte0, j, byte0 + 10, j);
			gb.drawString(String.valueOf((double) (area.height - j) / 50D), byte0 + 10, j);
		}

		gb.drawString("T", byte0 + 10, 15);
		clearPlot((int) ys);
	}

	void clearPlot(int i) {
		gb.setColor(bgColor);
		gb.fillRect(30, i, area.width - 31, area.height - i - 1);
		drawGravity(Color.red);
		gb.setColor(Color.red);
		gb.drawString("K=mv*v/2", 40, area.height - 1);
		gb.setColor(Color.blue);
		gb.drawString("U=mgh", 40, area.height - (int) ((g / 980D) * mass * P.length * (1.0D - Math.cos(((rk4) (P)).y[0]))));
		gf.drawImage(bgImage, 0, 0, this);
	}

	String d2String(double d) {
		float f = (float) ((double) (int) (d * 10D + 5D) / 10D);
		String s = String.valueOf(f);
		if (s.indexOf(".") == -1)
			s += ".0";
		return s;
	}

	void drawArrow(Graphics g, int i, int j, double d, double d1, String s) {
		int k = i + (int) (d * Math.cos(d1));
		int l = j + (int) (d * Math.sin(d1));
		double d2 = 0.52359877559829882D;
		g.drawLine(i, j, k, l);
		if (!running)
			g.drawString(s, k, l + 10);
		g.drawLine(k, l, k - (int) (d * 0.10000000000000001D * Math.cos(d1 + d2)), l - (int) (d * 0.10000000000000001D * Math.sin(d1 + d2)));
		g.drawLine(k, l, k - (int) (d * 0.10000000000000001D * Math.cos(d1 - d2)), l - (int) (d * 0.10000000000000001D * Math.sin(d1 - d2)));
	}

	void drawForce(int i, int j, double d, double d1, boolean flag) {
		if (!flag)
			gf.setColor(Color.blue);
		else
			gf.setColor(bgColor);
		d *= mass / 2D;
		drawArrow(gf, i, j, d, 1.5707963267948966D, "mg");
		if (!flag)
			gf.setColor(Color.green);
		drawArrow(gf, i, j, d * Math.sin(d1), 3.1415926535897931D - d1, "");
		drawArrow(gf, i, j, d * Math.cos(d1), 1.5707963267948966D - d1, "");
	}

	void drawGravity(Color color) {
		int i = 0;
		gb.setColor(color);
		drawArrow(gb, xmax, i, gravityCst * g, 1.5707963267948966D, "g");
		gb.drawString(String.valueOf(g), xmax - 30, 20 + i);
	}

	void drawMass(Color color) {
		int i = area.height / 2;
		gb.setColor(color);
		gb.drawLine(xmax, i, xmax, i += (int) (massCst * mass));
		gb.drawString("m=" + String.valueOf(mass), xmax - 30, 20 + i);
	}

	void drawPendulum(double d, double d1, boolean flag) {
		if (flag) {
			gf.drawImage(bgImage, 0, 0, this);
			return;
		}
		int i = (int) d;
		int j = (int) d1;
		double d2 = Math.atan((d - (double) x0) / (d1 - (double) y0));
		double d3 = gravityCst * g;
		if (dragging) {
			int k = 0;
			gf.setColor(Color.black);
			gf.drawLine(x0, y0, i, j + k);
			gf.fillOval(i - size, (j - size) + k, size2, size2);
			drawForce(i, j + k, d3, d2, flag);
		} else {
			gb.setColor(Color.red);
			int l = area.height - 2 - (int) ((mass * (P.length * P.length * ((rk4) (P)).y[1] * ((rk4) (P)).y[1])) / 1960D);
			gb.drawLine(i, l, i, l);
			l = (area.height + (int) ((g / 980D) * mass * (d1 - (double) y0 - P.length))) - size;
			gb.setColor(Color.blue);
			gb.drawLine(i, l, i, l);
			gf.setColor(Color.black);
			gf.drawLine(x0, y0, i, j);
			gf.fillOval(i - size3 / 2, j - size3 / 2, size3, size3);
			if (forceFlag || !running)
				drawForce(i, j, d3, d2, false);
			gf.setColor(Color.red);
			drawArrow(gf, i, j + 1, -0.10000000000000001D * P.length * ((rk4) (P)).y[1], 3.1415926535897931D - d2, "mv");
			gf.setColor(Color.black);
			gf.fillOval(i - size, l - size3, size3, size3);
		}
		repaint();
	}

	void drawPeroid(boolean flag, boolean flag1, int i, int j) {
		if (!flag) {
			gb.setColor(Color.black);
			gb.drawString("N=" + String.valueOf(count / 2), 40, 20);
			gb.drawString("T=" + String.valueOf((float) ((double) (int) (period * 20D) / 10D)) + " s", 40, 40);
			gb.drawString("L=" + String.valueOf((float) ((double) (int) (P.length * 10D) / 10D)), 40, 60);
		} else {
			gb.setColor(bgColor);
			gb.fillRect(40, 1, 55, 60);
		}
		if (!flag1) {
			gb.drawOval(i - 1, area.height - j - 1, 2, 2);
			gb.drawOval(2 * x0 - i - 1, area.height - j - 1, 2, 2);
		}
	}

	void init_plot() {
		clear();
		gb.setColor(Color.red);
		gb.fillOval(x0 - size, 0, size2, size2);
		running = false;
		drawGravity(Color.red);
		drawMass(Color.black);
		running = true;
		drawPendulum(xs, ys, false);
		gf.drawImage(bgImage, 0, 0, this);
		repaint();
	}

	void setpendulum(double d, double d1, boolean flag) {
		if (flag) {
			double d2 = Math.sqrt((d - (double) x0) * (d - (double) x0) + (d1 - (double) y0) * (d1 - (double) y0));
			double d3 = Math.asin((d - (double) x0) / d2);
			P.init(d2, d3, 0.0D);
			ys = d2 * Math.cos(d3);
		} else {
			P.init(Math.asin((d - (double) x0) / P.length), 0.0D);
		}
	}

	boolean validXY(int i, int j) {
		return (double) Math.abs(i - x0) < Math.max(P.length, 10D) && i < xmax;
	}

	void writeText(double d, double d1) {
		if (dragging)
			mouseP.setText("L="
					+ String.valueOf((float) ((double) (int) (P.length * 10D) / 10D))
					+ ", angle="
					+ String.valueOf((float) ((double) (int) ((Math.atan((d - (double) x0) / (d1 - (double) y0)) / 3.1415926535897931D) * 1800D) / 10D)));
		else if (Math.abs(d - (double) x0) < P.length)
			mouseP.setText("dx=" + String.valueOf(d - (double) x0) + ",T="
					+ String.valueOf((float) ((double) (int) (((double) area.height - d1) / 5D) / 10D)) + "s, theta="
					+ d2String(cst * Math.asin((d - (double) x0) / P.length)));
	}
}
